#pragma once

#include "common.h"
#include "ntdseq.h"
#include "ntdtime.h"

/*
Protocols   
--------------------------------------------------------------------------------------

protocol

//    0                   1                   2                   3   
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  0 |                               PacketSize                      |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  1 |   Ver | Type  | Reserved      |        Conv_ID                |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                                                               |
//    |                               Body                            |
//    |                                                               |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

PacketSize = BodyLen + HeaderSize(8 Bytes)

Type:

0x0 - HandShake

//    0                   1                   2                   3   
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                  Initial sequence number                      |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


0x1 - HandShakeResp
//    0                   1                   2                   3   
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                  Initial sequence number                      |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      Server Load                              |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

0x2 - Fin
None

0x3 - FinResp
//    0                   1                   2                   3   
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  1 |   Ver | 0x3   |c (Reserved)   |        Conv_ID                |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      receiving rate                           |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      loss rate                                |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

0x4 - Data

//    0                   1                   2                   3   
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                  Packet sequence number                       |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      Timestamp sending                        |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                       Data                                    |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


0x5 - FeedBack

//    0                   1                   2                   3   
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                  Packet sequence number                       |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      Timestamp sending                        |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      Timestamp receiving                      |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      receiving rate                           |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      lossCnt                                  |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

0x4 - RawData

//    0                   1                   2                   3   
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                       Data                                    |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


Other control packets  will be defined later.

*/

template <typename T>
T random(T max)
{
	static int32 s = NtdTime::Instance()->GetCurrentTime_ms();
	srand(s++);
	return static_cast<T>(max * (double(rand()) / RAND_MAX));
}

#define RANDOM_32() random<int32>(INT_MAX)
#define RANDOM_16() random<int32>(SHRT_MAX)

typedef enum
{
	CODEC_ERROR  =-1,
	CODEC_NOLEN  = 0,
	CODEC_SUCCESS = 1,
} CodecStatus;

//#define MAX_UDPMSG_SIZE 1400;
//#define MAX_TCPMSG_SIZE 0xFFFF;
//#define HEAD_LEN 8

class NtdProtoHead
{
public:
	static const uint32 HEAD_LEN = 8;

	enum ProtoType{
		UNKNOWN = -1,
		HANDSHAKE = 0x0,
		HANDSHAKERESP =0x1,
		FIN = 0x2,
		FINRESP = 0x3,
		DATA = 0x4,
		FEEDBACK =0x5,
		RAWDATA = 0x6,
	};

	NtdProtoHead()
	{
		m_packetSize = 0;
		m_version = 0;
		m_type = UNKNOWN;
		m_reserv = 0;
		m_convId = 0;
	}

	CodecStatus EncodeHead(evbuffer* buf);
	CodecStatus DecodeHead(evbuffer* buf);

	uint32 m_packetSize;
	uint8 m_version;
	uint8 m_type;
	uint8 m_reserv;
	uint16 m_convId;
};

class NtdProto
{
public:
	virtual CodecStatus Encode(evbuffer* buf) = 0;
	virtual CodecStatus Decode(evbuffer* buf) = 0;
	virtual std::string Desc() = 0;
	virtual uint32 GetPaketSize() = 0;

	//static bool ParseProtocol(NtdProto*& proto, evbuffer* buf);
	uint16 m_convId;
};

class NtdProtoHandShake : public NtdProto
{
public:
	NtdProtoHandShake()
	{
		// Random Initial Sequence Number
		//srand(NtdTime::Instance()->GetCurrentTime_ms());
		//m_ini_seq = static_cast<int32>(NtdSeq::m_iMaxSeqNo * (double(rand()) / RAND_MAX));
        //m_ini_seq = RANDOM_32();
		m_ini_seq = 0;
		m_bPacketPair = 0;
	}

	CodecStatus Encode(evbuffer* buf);
	CodecStatus Decode(evbuffer* buf);
	virtual std::string Desc();
	virtual uint32 GetPaketSize();

	int32 m_ini_seq;
	bool m_bPacketPair;
};

class NtdProtoHandShakeResp : public NtdProto
{
public:
	NtdProtoHandShakeResp()
	{
		m_ini_seq = 0;
		m_load = 0;
	}

	CodecStatus Encode(evbuffer* buf);
	CodecStatus Decode(evbuffer* buf);
	virtual std::string Desc();
	virtual uint32 GetPaketSize();

	int32 m_ini_seq;
	int32 m_load;//no define yet
};

class NtdProtoFin : public NtdProto
{
public:
	CodecStatus Encode(evbuffer* buf);
	CodecStatus Decode(evbuffer* buf);
	virtual std::string Desc();
	virtual uint32 GetPaketSize();
};

class NtdProtoFinResp: public NtdProto
{
public:
	NtdProtoFinResp()
	{
		m_bCongestion = false;
		m_recv_rate = 0;
		m_pkt_loss = 0;
	}
	CodecStatus Encode(evbuffer* buf);
	CodecStatus Decode(evbuffer* buf);
	virtual std::string Desc();
	virtual uint32 GetPaketSize();

	bool m_bCongestion;
	uint32 m_recv_rate;
	uint32 m_pkt_loss;
};

class NtdProtoData : public NtdProto
{
public:
	NtdProtoData()
	{
		m_ptk_seq = 0;
		m_timestamp_sending = 0;
		m_pData = NULL;
		m_len = 0;
	}

	NtdProtoData(uint32 len)
	{
		m_ptk_seq = 0;
		m_timestamp_sending = 0;
		m_pData = NULL;
		m_len = len - sizeof(m_ptk_seq) - sizeof(m_timestamp_sending);
	}

	CodecStatus Encode(evbuffer* buf);
	CodecStatus Decode(evbuffer* buf);
	virtual std::string Desc();
	virtual uint32 GetPaketSize();

	int32 m_ptk_seq;
	uint32 m_timestamp_sending;
	byte * m_pData;
	uint32 m_len;
};

class NtdProtoFeedback: public NtdProto
{
public:
	NtdProtoFeedback()
	{
		m_ptk_seq = 0;
		m_timestamp_sending = 0;
		m_timestamp_recving = 0;
		m_recving_rate = 0;
		m_pkt_loss = 0;
	}

	CodecStatus Encode(evbuffer* buf);
	CodecStatus Decode(evbuffer* buf);
	virtual std::string Desc();
	virtual uint32 GetPaketSize();

	int32 m_ptk_seq;
	uint32 m_timestamp_sending;
	uint32 m_timestamp_recving;
	uint32 m_recving_rate;
	uint32 m_pkt_loss;
};

class NtdProtoRawData : public NtdProto
{
public:
	NtdProtoRawData()
	{
		m_pData = NULL;
		m_len = 0;
	}

	NtdProtoRawData(uint32 len)
	{
		m_pData = NULL;
		m_len = len;
	}

	CodecStatus Encode(evbuffer* buf);
	CodecStatus Decode(evbuffer* buf);
	virtual std::string Desc();
	virtual uint32 GetPaketSize();

	byte * m_pData;
	uint32 m_len;
};

class NtdProtoParser
{
public:
	bool ParseProtocol(evbuffer* buf, void* pContext);
	virtual void HandleProtoData(NtdProtoData * proto, void* pContext) { LOG4CPLUS_TRACE(g_logger,proto->Desc()); }
	virtual void HandleProtoRawData(NtdProtoRawData * proto, void* pContext) { LOG4CPLUS_TRACE(g_logger,proto->Desc()); }
	virtual void HandleProtoFeedback(NtdProtoFeedback * proto, void* pContext) { LOG4CPLUS_TRACE(g_logger,proto->Desc()); }
	virtual void HandleHandShake(NtdProtoHandShake * proto, void* pContext) { LOG4CPLUS_TRACE(g_logger,proto->Desc()); }
	virtual void HandleHandShakeResp(NtdProtoHandShakeResp * proto, void* pContext) { LOG4CPLUS_TRACE(g_logger,proto->Desc()); }
	virtual void HandleFin(NtdProtoFin * proto, void* pContext) { LOG4CPLUS_TRACE(g_logger,proto->Desc()); }
	virtual void HandleFinResp(NtdProtoFinResp * proto, void* pContext) { LOG4CPLUS_TRACE(g_logger,proto->Desc()); }
};